﻿using System;
using System.IO;

namespace ZeroBugBounce.Missive
{
	/// <summary>
	/// Properly implements the APM for FileStream
	/// </summary>
	public class AsyncFileWriteHandler : Handler<AsyncFileWriteRequest>
	{
		public override void Handle(HandlerContext<AsyncFileWriteRequest> context)
		{
			string path = context.Message.Path;
			var bytesToWrite = context.Message.BytesToWrite;
			FileMode mode = context.Message.FileMode;

			var writeStream = new FileStream(path, mode, FileAccess.Write, FileShare.Read, 4096, FileOptions.Asynchronous);
			writeStream.BeginWrite(bytesToWrite, 0, bytesToWrite.Length, WriteCallback, writeStream);
		}

		void WriteCallback(IAsyncResult ar)
		{
			var writeStream = (FileStream)ar.AsyncState;
			writeStream.EndWrite(ar);
		}
	}

	public class AsyncFileWriteHandler<TReply> : Handler<AsyncFileWriteRequest>
	{
		Func<AsyncFileWriteRequest, TReply> replyAction;
		public AsyncFileWriteHandler(Func<AsyncFileWriteRequest, TReply> createReplyAction)
		{
			replyAction = createReplyAction;
		}

		public override void Handle(HandlerContext<AsyncFileWriteRequest> context)
		{
			string path = context.Message.Path;
			var bytesToWrite = context.Message.BytesToWrite;
			FileMode mode = context.Message.FileMode;

			var writeStream = new FileStream(path, mode, FileAccess.Write, FileShare.Read, 4096, FileOptions.Asynchronous);			
			writeStream.BeginWrite(bytesToWrite, 0, bytesToWrite.Length, WriteCallback, 
				new Tuple<FileStream, HandlerContext<AsyncFileWriteRequest>>(writeStream, context));
		}

		void WriteCallback(IAsyncResult ar)
		{
			var stateTuple = (Tuple<FileStream, HandlerContext<AsyncFileWriteRequest>>)ar.AsyncState;
			var writeStream = stateTuple.Item1;
			var replyContext = stateTuple.Item2 as ReplyHandlerContext<AsyncFileWriteRequest, TReply>;

			writeStream.EndWrite(ar);

			if (replyContext != null && replyContext.ReplyCallback != null && replyAction != null)
			{
				replyContext.ReplyCallback(replyAction(replyContext.Message));
			}
		}
	}

	public class AsyncFileWriteRequest
	{
		public AsyncFileWriteRequest(string path, FileMode mode, Byte[] bytesToWrite)
		{
			Path = path;
			FileMode = mode;
			BytesToWrite = bytesToWrite;
		}

		public string Path { get; private set; }
		public FileMode FileMode { get; private set; }
		public Byte[] BytesToWrite { get; private set; }
	}
}
